import json
from datetime import datetime
import os
from PIL import ImageFont

from src.rating import calculate_rating, calculate_rating_by_score
from src.draw_box import draw_box
from src.data import load_csv, load_json, save_to_csv
from src import graphics


class Player:
    """
    玩家类,用于管理玩家的成绩数据并生成成绩图片
    
    Attributes:
        name (str): 玩家名称
        isCsv (bool): 数据来源是否为CSV文件
        best_score_list (list): 最佳成绩列表
        best_rating (float): 最佳Rating值
        recent_score (list, optional): 最近成绩列表(仅JSON数据源)
        recent_rating (float, optional): 最近Rating值(仅JSON数据源) 
        rating (float, optional): 当前Rating值(仅JSON数据源)
    """

    def __init__(self, name: str, csv_path: str = None, json_path: str = None):
        """
        初始化Player对象
        
        Args:
            name: 玩家名称
            csv_path: CSV文件路径,如果为None则从JSON加载数据
            json_path: JSON文件路径,仅在csv_path为None时使用
        """
        self.isCsv = bool(csv_path)
        self.name = name

        if csv_path:
            self.best_score_list, self.best_rating = load_csv(csv_path)
            save_to_csv(csv_path, self.best_score_list)
        else:
            if not json_path:
                raise ValueError("Either csv_path or json_path must be provided")

            rating = load_json(
                csv_output_path=f'score/{self.name}_from_json.csv',
                json_input_path=json_path
            )
            self.best_score_list = rating['data']['base_rating_list']
            self.recent_score = rating['data']['hot_rating_list']
            self.best_rating = rating['data']['base_rating']
            self.recent_rating = rating['data']['hot_rating']
            self.rating = rating['data']['rating']

    def print_b30(self):
        """
        生成并保存Best 30成绩图片
        
        生成的图片包含:
        - 玩家名称
        - Best 30成绩列表
        - 数据来源标记(CSV/JSON)
        """
        # 设置基础参数
        top = 200
        width, height = 1764, 2160 + top

        # 创建基础图片
        pic, draw = graphics.create_base(width, height)

        # 绘制玩家名称
        draw.text((width / 2, top / 2 - 20), self.name,
                  fill=(0, 0, 0),
                  font=ImageFont.truetype('assets/fonts/LFAXI.TTF', 120),
                  anchor='mm')

        # 绘制Rating值
        base_rating = graphics.create_rating_image(self.best_rating * 100 if self.best_rating < 100 else self.best_rating)
        base_rating = base_rating.resize((int(base_rating.size[0] * 2), int(base_rating.size[1] * 2)))
        pic.paste(base_rating, (800, 163))

        # 绘制成绩展示区域背景
        graphics.draw_rounded_rectangle(draw, (50, top + 30, width - 50, height - 30), 20, fill=(255, 165, 0))
        draw.rectangle((100, top + 80, width - 100, height - 80), fill=(225, 125, 0))

        # 绘制成绩格子
        for i in range(0, 10):
            for j in range(0, 3):
                draw_box(draw, pic, 120 + 515 * j, top + 100 + 198 * i, 495, 178,
                         self.best_score_list[i * 3 + j], is_b30=True)

        # 绘制数据来源标记
        source_text = "Data from CSV" if self.isCsv else "Data from JSON"
        draw.text((width - 5, height - 15), source_text,
                  fill=(0, 0, 0),
                  font=ImageFont.truetype('assets/fonts/LFAXI.TTF', 20),
                  anchor='rm')

        # 保存图片
        pic = pic.convert("RGB")
        self._save_image(pic, "")

    def print_b30_r10(self):
        """
        生成并保存Best 30 + Recent 10成绩图片(已弃用)
        
        生成的图片包含:
        - 玩家名称
        - Best 30成绩列表
        - Recent 10成绩列表
        - 当前Rating、Base Rating、Hot Rating
        - 数据来源标记(CSV/JSON)
        """
        if not hasattr(self, 'recent_score'):
            print('No recent score found, please update the rating.json file first')
            return

        # 创建基础图片
        pic, draw = graphics.create_base(1764, 2480)

        # 绘制玩家名称
        draw.text((882, 150), self.name,
                  fill=(0, 0, 0),
                  font=ImageFont.truetype('assets/fonts/LFAXI.TTF', 150),
                  anchor='mm')

        # 绘制三个Rating值
        self._draw_ratings(pic, draw)

        # 绘制成绩展示区域背景
        graphics.draw_rounded_rectangle(draw, (50, 350, 1714, 2450), 20, fill=(255, 165, 0))
        draw.rectangle((100, 400, 1263, 2400), fill=(225, 125, 0))
        draw.rectangle((1263, 400, 1664, 2400), fill=(175, 85, 0))

        # 绘制Best 30成绩格子
        for i in range(0, 10):
            for j in range(0, 3):
                draw_box(draw, pic, 120 + 381 * j, 420 + 198 * i, 361, 178,
                         self.best_score_list[i * 3 + j], is_b30=False)

        # 绘制Recent 10成绩格子
        for i in range(0, 10):
            draw_box(draw, pic, 1283, 420 + 198 * i, 361, 178,
                     self.recent_score[i], is_b30=False)

        # 绘制数据来源标记
        source_text = "Data from CSV" if self.isCsv else "Data from JSON"
        draw.text((1764 - 5, 2480 - 15), source_text,
                  fill=(0, 0, 0),
                  font=ImageFont.truetype('assets/fonts/LFAXI.TTF', 20),
                  anchor='rm')

        # 保存图片
        pic = pic.convert("RGB")
        self._save_image(pic, "_b30r10")

    def update_csv(self, target_path: str):
        """
        更新CSV文件中的成绩数据
        
        Args:
            target_path: 目标CSV文件路径
        """
        # 加载音乐数据
        with open('assets/music.json', 'r', encoding='utf-8') as f:
            music = json.load(f)

        # 读取CSV数据
        with open(target_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()

        # 解析并处理成绩数据
        rating_list = self._process_csv_data(lines[1:], music)

        # 合并现有最佳成绩
        for score in self.best_score_list:
            rating_list.append(self._convert_score_format(score))

        # 排序并去重
        rating_list.sort(key=lambda x: (calculate_rating(x['constant'], int(x['score']))), reverse=True)
        rating_list = self._remove_duplicates(rating_list)

        # 保存更新后的数据
        save_to_csv(target_path, rating_list[:30])

    def _draw_ratings(self, pic, draw):
        """
        绘制三个Rating值(Base/Current/Hot)
        
        Args:
            pic: PIL Image对象
            draw: PIL ImageDraw对象
        """
        font_rating = ImageFont.truetype('cambria', 80)

        # 绘制Base Rating
        base_rating = graphics.create_rating_image(self.best_rating)
        base_rating = base_rating.resize((int(base_rating.size[0] * 2.5), int(base_rating.size[1] * 2.5)))
        pic.paste(base_rating, (460, 260))

        # 绘制Hot Rating
        hot_rating = graphics.create_rating_image(self.recent_rating)
        hot_rating = hot_rating.resize((int(hot_rating.size[0] * 2.5), int(hot_rating.size[1] * 2.5)))
        pic.paste(hot_rating, (1100, 260))

        # 绘制Current Rating
        current_rating = graphics.create_rating_image(self.rating)
        current_rating = current_rating.resize((int(current_rating.size[0] * 2.5), int(current_rating.size[1] * 2.5)))
        pic.paste(current_rating, (780, 260))

        # 绘制箭头
        draw.text((700, 240), '>', fill=(0, 0, 0), font=font_rating)
        draw.text((1020, 240), '<', fill=(0, 0, 0), font=font_rating)

    def _save_image(self, pic, suffix: str):
        """
        保存图片到cache目录
        
        Args:
            pic: PIL Image对象
            suffix: 文件名后缀
        """
        # 确保cache目录存在
        cache_dir = 'cache'
        if not os.path.exists(cache_dir):
            os.makedirs(cache_dir)

        # 根据操作系统选择日期格式
        if os.name == 'posix':
            date_format = "%Y年%-m月%-d日"
        elif os.name == 'nt':
            date_format = "%Y年%#m月%#d日"
        else:
            raise Exception('Unsupported OS')

        # 生成文件名并保存
        source = "csv" if self.isCsv else "json"
        filename = f'cache/{datetime.now().strftime(date_format)}_{source}{suffix}.jpg'
        pic.save(filename)

    def _process_csv_data(self, lines, music):
        """
        处理CSV文件中的成绩数据
        
        Args:
            lines: CSV文件内容行
            music: 音乐数据字典
            
        Returns:
            list: 处理后的成绩数据列表
        """
        rating_list = []
        for line in lines:
            song, difficulty, score, rating = line.split(',')
            constant = self._get_song_constant(song, difficulty, music)
            rating_list.append({
                'music': {
                    'name': song,
                    'jacket': music[song]['jacket'] if song in music else '',
                    'artist': music[song]['artist'] if song in music else '',
                },
                'difficulty': difficulty,
                'score': int(score),
                'rating': float(round(graphics.truncate_two_decimal_places(
                    calculate_rating(constant, int(score))) * 100)),
                'constant': constant,
            })
        return rating_list

    def _get_song_constant(self, song: str, difficulty: str, music: dict) -> float:
        """
        获取歌曲定数
        
        Args:
            song: 歌曲名称
            difficulty: 难度
            music: 音乐数据字典
            
        Returns:
            float: 歌曲定数,如果未找到则返回0
        """
        if song not in music:
            print(f"ALERT: {song} not in music.json")
            return 0
        elif difficulty not in music[song]:
            print(f"ALERT: {difficulty} not in {song}")
            return 0
        return music[song][difficulty]

    def _convert_score_format(self, score):
        """
        转换成绩数据格式
        
        Args:
            score: 原始成绩数据
            
        Returns:
            dict: 转换后的成绩数据
        """
        # 如果difficulty是数字索引,转换为对应的难度名称
        difficulty = (graphics.difficulty_mapping_up[score['difficulty']]
                      if isinstance(score['difficulty'], int)
                      else score['difficulty'])

        return {
            'music': {
                'name': score['music']['name'],
                'jacket': score['music'].get('jacket', ''),
                'artist': score['music'].get('artist', ''),
            },
            'difficulty': difficulty,
            'score': score['score'],
            'rating': float(score['rating']),
            'constant': calculate_rating_by_score(score['score'], float(score['rating']) / 100)
        }

    def _remove_duplicates(self, rating_list):
        """
        移除重复的歌曲成绩,保留最高分
        
        Args:
            rating_list: 成绩列表
            
        Returns:
            list: 去重后的成绩列表
        """
        name_set = set()
        new_rating_list = []
        for score in rating_list:
            if score['music']['name'] not in name_set:
                name_set.add(score['music']['name'])
                new_rating_list.append(score)
        return new_rating_list
